//
// Created by root on 4/29/17.
//

#ifndef FUNNY_RUNNER_SCHEME_H
#define FUNNY_RUNNER_SCHEME_H

#include <stdio.h>
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>

#include "../src/ds/datastructure.h"
#include "../src/basic/operator.h"



#ifdef cmocka_run_group_tests
#undef cmocka_run_group_tests
#endif
# define cmocka_run_group_tests(group_tests, group_setup, group_teardown) \
        _cmocka_run_group_tests(#group_tests, group_tests, sizeof(group_tests) / sizeof(group_tests[0]), group_setup, group_teardown)

/**
 * macro definition of scheme stubs
 */
#define WRAP_SCHEME_OP0(func) Cell* __wrap_ ## func \
    (Scheme *sc); \
Cell* __wrap_ ## func \
    (Scheme *sc) {return mock_ptr_type(Cell*);}

#define WRAP_SCHEME_OP1(func) Cell* __wrap_ ## func \
    (Scheme *sc, Cell* x); \
Cell* __wrap_ ## func \
    (Scheme *sc, Cell* x) {check_expected(sc);check_expected(x);return mock_ptr_type(Cell*);}

#define WRAP_SCHEME_OP2(func) Cell* __wrap_ ## func \
    (Scheme *sc, Cell* x, Cell* y); \
Cell* __wrap_ ## func \
    (Scheme *sc, Cell* x, Cell* y) {check_expected(sc);check_expected(x);check_expected(y);return mock_ptr_type(Cell*);}

#define WRAP_SCHEME_OP(func) WRAP_SCHEME_OP0(func)




#define assert_cell_equal(_cell1,_cell2) assert_true(equal(global_scheme, _cell1, _cell2))
// #define assert_cell_equal1(_cell1,_cell2) assert_memory_equal(_cell1, _cell2, sizeof(Cell))

#define ASSERT_OP_IN_STACK(_op) assert_ptr_equal(pick_callstack_item(global_scheme, ITEM_OPERATOR), cell_op_##_op)

#define ASSERT_OP_CURRENT(_op) assert_ptr_equal(pick_current_item(global_scheme, ITEM_OPERATOR), cell_op_##_op)
#define ASSERT_CODE_CURRENT(_code) assert_memory_equal(pick_current_item(global_scheme, ITEM_CODE), _code, sizeof(Cell))

#define ASSERT_EMPTY_OP_IN_STACK assert_ptr_equal(pick_callstack_item(global_scheme, ITEM_OPERATOR), G_NIL)

#define EVAL_CODE_AND_RETURN(_code,_result) assert_cell_equal(global_scheme->code, _code); \
    global_scheme->returnValue = _result; \
    pop_callstack(global_scheme)


#define LIST_1(_a)                cons(global_scheme, _a, G_NIL)
#define LIST_2(_a,_b)             cons(global_scheme, _a, LIST_1(_b))
#define LIST_3(_a,_b,_c)          cons(global_scheme, _a, LIST_2(_b,_c))
#define LIST_4(_a,_b,_c,_d)       cons(global_scheme, _a, LIST_3(_b,_c,_d))
#define LIST_5(_a,_b,_c,_d,_e)    cons(global_scheme, _a, LIST_4(_b,_c,_d,_e))
#define LIST_6(_a,_b,_c,_d,_e,_f) cons(global_scheme, _a, LIST_5(_b,_c,_d,_e,_f))

#define TEST_INVALID_TYPE_EXCEPTION(_r) assert_string_equal(_r->_type, EXCEPTION_TYPE); \
    assert_string_equal(car(_r)->_string, "INVALID_TYPE")

#define TEST_DIVISION_BY_ZERO(_r) assert_true(eqe(_r, E_DIVISION_BY_ZERO))

Scheme *global_scheme;
int setup_scheme(void **state);
int teardown_scheme(void **state);
String interpret(String statement);

#endif //FUNNY_RUNNER_SCHEME_H
